#ifndef __DB_POOL_HPP__
#define __DB_POOL_HPP__

#include <string>
#include <map>
#include <vector>
#include <stdexcept>
#include <tuple>
#include <memory>
#include <list>
#include <mutex>
#include <condition_variable>
#include <boost/core/noncopyable.hpp>

#include <sqlite3.h>

constexpr int DB_MIN_CONN_CNT = 1;


const std::vector<std::map<std::string, std::string>> gDBConfig
{
	{
		{"pool_name", "test"},
		{"db_path", "/db/test.db"},
		{"db_max_conn_cnt", "4"}
	}
};



/// @brief 返回结果 封装了通过字段名去获取值的方法
class DBResultSet : private boost::noncopyable
{
public:
	using string   		= std::string;
	using ResultMap 	= std::map<string, string>;
	using ResultVec		= std::vector<ResultMap*>;
private:
	ResultVec  resVec_;
public:
	friend int db_result_callback(void *ResultSet, int argc, char **argv, char **azColName);
public:
	DBResultSet() = default;
	virtual ~DBResultSet();
	size_t	getResultNum() const { return resVec_.size(); }
	size_t	getColNum(int index) const;
	int 	getInt(const string& key, int index) noexcept(false);
	float 	getFloat(const string& key, int index) noexcept(false);
	const string& getString(const string& key, int index) noexcept(false);
private:
	const string& findValue_(const string &key, int index) const;
	void 	addResultMap_(ResultMap *map) { resVec_.emplace_back(map); }
};

using DBResultSetPtr = std::shared_ptr<DBResultSet>;

extern int db_result_callback(void *ResultSet, int argc, char **argv, char **azColName);


class DBPrepareStatement
{
private:
	sqlite3_stmt   *stmt_;
	uint32_t		param_cnt_;

public:
	DBPrepareStatement() { stmt_ = nullptr; param_cnt_ = 0u; }
	virtual ~DBPrepareStatement();
	bool	init(sqlite3 *db, std::string &sql);
	bool 	setParam(int index, int param);
	bool	setParam(int index, const std::string& text);
	auto	execute();
	bool	reset();
private:

};



class DBConn
{
private:
	sqlite3 	*db_;
	std::string  db_path_;
	std::string  pool_name_;
public:
	DBConn(const std::string&  pool_name, const std::string& db_path) :
		pool_name_(pool_name), db_path_(db_path)
	{}
	virtual ~DBConn();
	int		init();
	bool	startTransaction();
	bool 	commit();
	bool 	rollback();
	DBResultSetPtr executeQuery(const std::string &sql);
	bool	executeUpdate(const std::string &sql);
public:
	const std::string& getPoolName() const { return pool_name_; }
};


using DBConnPtr = std::shared_ptr<DBConn>;

class DBPool : private boost::noncopyable
{
public:
	using ListT = std::list<DBConn *>;
private:
	std::string pool_name_;
	bool		pool_abort_;
	std::string db_path_;
	int			db_cur_conn_cnt_;		// 当前启用的连接数量
	int			db_max_conn_cnt_;		// 最大连接数量
	ListT		free_list_;				// 空闲的连接
	std::mutex	mutex_;
	std::condition_variable	cond_var_;
public:
	DBPool() = default;
	DBPool(const char *pool_name, const char *db_name, const char *db_path, int max_conn_cnt) :
		pool_name_(pool_name), db_path_(db_path), db_max_conn_cnt_(max_conn_cnt)
	{ 
		db_cur_conn_cnt_ =  DB_MIN_CONN_CNT; 
		pool_abort_ = false;
	}
	virtual ~DBPool();
	int init();
	DBConn *getDBConn(const uint32_t timeout = 0);
	void 	relDBConn(DBConn *pConn);
public:
	const std::string& getPoolName() const	{ return pool_name_; }
	const std::string& getDBPath() const 	{ return db_path_; }
};



class DBManager : private boost::noncopyable
{
private:
	std::map<std::string, DBPool *> pool_map_;
public:
	virtual ~DBManager();
	DBConn *getDBConn(const std::string &pool_name, uint32_t timeout = 0);
	void 	relDBConn(DBConn * pConn);
	static DBManager& getInstance();
private:
	DBManager();
};


class AutoRelDBConn
{
private:
	DBManager 	*manager_;
	DBConn 		*conn_;
public:
	AutoRelDBConn(DBManager *manager, DBConn *conn) : 
		manager_(manager), conn_(conn) 
	{}
	~AutoRelDBConn()
	{
		if (manager_) {
			manager_->relDBConn(conn_);
		}
	}

};

#define AUTO_REAL_DBCONN(m,c) AutoRelDBConn autoreldbconn(m,c)


#endif // !__DB_POOL_HPP__